#include "stdafx.h"
#include "csArchiveSystem.h"
#include "csFileArchive.h"
#include "csZipArchive.h"
#include "csFileStream.h"

//------------------------------------------------------------------------
csArchiveSystem::csArchiveSystem()
{

	tc dict[csFILE_SYSTEM_MAX_PATH];
#ifdef _UNICODE
	#ifdef _WIN32
	#if !defined ( _WIN32_WCE )
		_wgetcwd(dict, csFILE_SYSTEM_MAX_PATH);
	#endif
	#else
		getcwd(dict, csFILE_SYSTEM_MAX_PATH);
	#endif
#else
	#ifdef _WIN32
	#if !defined ( _WIN32_WCE )
		_getcwd(dict, csFILE_SYSTEM_MAX_PATH);
	#endif
	#else
		getcwd(dict, csFILE_SYSTEM_MAX_PATH);
	#endif
#endif
	m_workingDictionary = dict;
}

//------------------------------------------------------------------------
csArchiveSystem::~csArchiveSystem()
{

}

//------------------------------------------------------------------------
bool
csArchiveSystem::SetSystemWorkingDir(const csString& path)
{
	bool success=false;
#ifdef _UNICODE
	#ifdef _MSC_VER
	#if !defined ( _WIN32_WCE )
		success=(_wchdir(path.c_str()) == 0);
	#endif
	#else
		csASSERT(false);
		//success=(chdir(path.c_str()) == 0);
	#endif
#else
	#ifdef _MSC_VER
	#if !defined ( _WIN32_WCE )
		success=(_chdir(path.c_str()) == 0);
	#endif
	#else
		success=(chdir(path.c_str()) == 0);
	#endif
#endif
	return success;
}

//------------------------------------------------------------------------
csString
csArchiveSystem::ConcatenatePath(const csString& base, const csString& path)
{
	if (base.empty() || IsAbsolutePath(path))
		return path;
	else
		return base + csT("/") + path;
}

//------------------------------------------------------------------------
csString
csArchiveSystem::GetFileName(const csString& path)
{
	s32 idx = path.rfind(csT('\\'));
	if(path.rfind(csT('/'))>idx)
	{
		idx = path.rfind(csT('/'));
	}
	return path.subString(idx+1);
}

//------------------------------------------------------------------------
csString
csArchiveSystem::GetAbsolutePath(const csString& path)
{
	tc *p=0;

#ifdef _WIN32
#if !defined ( _WIN32_WCE )
	#ifdef _UNICODE
		tc fpath[_MAX_PATH];
		p = _wfullpath( fpath, path.c_str(), _MAX_PATH);
	#else
		tc fpath[_MAX_PATH];
		p = _fullpath( fpath, path.c_str(), _MAX_PATH);
	#endif
#endif
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
	csASSERT(false);
	//c8 fpath[4096];
	//fpath[0]=0;
	//p = realpath( path.c_str(), fpath);
	//if (!p)
	//{
	//	// content in fpath is undefined at this point
	//	if ('0'==fpath[0]) // seems like fpath wasn't altered
	//	{
	//		// at least remove a ./ prefix
	//		if ('.'==path[0] && '/'==path[1])
	//			return path.subString(2, path.length()-2);
	//		else
	//			return path;
	//	}
	//	else
	//		return csString(fpath);
	//}
#endif
	return csString(fpath);
}

//------------------------------------------------------------------------
bool
csArchiveSystem::IsFileExist(const csString& path)
{
#ifdef _WIN32
	#ifdef _UNICODE
		return -1==_waccess(path.c_str(),0);
	#else
		return -1==_access(path.c_str(),0);
	#endif
#else
	csASSERT(false);
    //return -1==access(path.c_str(), 0);
#endif
}

//------------------------------------------------------------------------
bool
csArchiveSystem::IsAbsolutePath(const csString& path)
{
#ifdef _WIN32
	#ifdef _UNICODE
		return (iswalpha(path[0]) && path[1] == csT(':'));
	#else
		return (isalpha(path[0]) && path[1] == csT(':'));
	#endif
#else
	csASSERT(false);
	return path[0] == '/' || path[0] == '\\';
#endif
}

//------------------------------------------------------------------------
bool
csArchiveSystem::IsArchivePath(const csString& path)
{
	u32 idx = path.find(csT(':'));
	if(idx==csINVALID_INDEX||idx<=2)
	{
		return false;
	}
	for(u32 i=0;i<idx;i++)
	{
		if(path[idx]==csT(',')||path[idx]==csT('/')||path[idx]==csT('\\'))
		{
			return false;
		}
	}
	return true;
}

//------------------------------------------------------------------------
bool
csArchiveSystem::IsReverseDir(const csString& path)
{
	return (path[0] == '.' && (path[1] == 0 || (path[1] == '.' && path[2] == 0)));

}

//------------------------------------------------------------------------
bool
csArchiveSystem::IsCaseSensitive()
{
#ifdef _WIN32
	return true;
#else
	return false;
#endif
}

//------------------------------------------------------------------------
csStream*
csArchiveSystem::OpenAbsoluteStream(const csString &path, csEStreamAccessType type)
{
#ifdef UNICODE
	FILE* hFile = _wfopen(path.c_str(),csESAT_READ==type?csT("rb"):(csESAT_WRITE==type?csT("wb"):csT("ab")));
	if(csNULL==hFile)
	{
		return csNULL;
	}
#else
	FILE* hFile = fopen(path.c_str(),csESAT_READ==type?csT("rb"):(csESAT_WRITE==type?csT("wb"):csT("ab")));
	if(csNULL==hFile)
	{
		return csNULL;
	}
#endif
	return csNEW csFileStream(path,hFile);
}

//------------------------------------------------------------------------
csArray<const csIArchive*>
csArchiveSystem::findMountedArchiveByPath(const csString& path) const
{
	csArray<const csIArchive*> arcs;
	csMountedArchives::const_iterator it = m_mountedArchives.begin();
	for(;it!=m_mountedArchives.end();++it)
	{
		if(path==it.value()->getFullPath())
		{
			arcs.push_back((it.value()));
		}
	}
	return arcs;
}

//------------------------------------------------------------------------
void
csArchiveSystem::SaveAbsoluteStream(const csString &path, csStream &stream)
{
#ifdef _UNICODE
	FILE* hFile = _wfopen(path.c_str(),csT("wb"));
	s32 fSize = stream.getSize();
#else
	FILE* hFile = fopen(path.c_str(),csT("wb"));
	s32 fSize = stream.getSize();
#endif
	if(csNULL==hFile||0==fSize)
	{
		return;
	}
	void* buffer = malloc(fSize);
	stream.read(buffer,fSize);
	fseek(hFile,0,SEEK_SET);
	fwrite(buffer, 1, stream.getSize(), hFile);
	free(buffer);
	fclose(hFile);
}

//------------------------------------------------------------------------
const csIArchive*
csArchiveSystem::findMountedArchiveByUrl(const csString& url) const
{
	csIArchive* const*  val = m_mountedArchives.findValue(url);
	return csNULL==val?csNULL:*val;
}

//------------------------------------------------------------------------
csStream*
csArchiveSystem::open(const csString& path, csEStreamAccessType type)
{
	if(IsArchivePath(path))
	{
		s32 idx = path.find(':');
		csASSERT(csINVALID_INDEX!=idx);
		csASSERT(idx<path.length());
		csIArchive** arc = m_mountedArchives.findValue(path.subString(0,idx+1));
		if(csNULL==arc)
		{
			return csNULL;
		}
		csASSERT(csNULL!=arc);
		return (*arc)->open(path.subString(idx+1,path.length()-idx-1),type);
	}
	else if(IsAbsolutePath(path))
	{
		return OpenAbsoluteStream(path,type);
	}
	return OpenAbsoluteStream(m_workingDictionary+path,type);
}

//------------------------------------------------------------------------
void
csArchiveSystem::save(const csString& path, csStream& stream)
{
	if(IsArchivePath(path))
	{
		s32 idx = path.find(':');
		csASSERT(csINVALID_INDEX!=idx);
		csASSERT(idx<path.length());
		csIArchive** arc = m_mountedArchives.findValue(path.subString(0,idx+1));
		if(csNULL==arc)
		{
			return;
		}
		csASSERT(csNULL!=arc);
		(*arc)->save(path.subString(idx+1,path.length()-idx-1),stream);
		return;
	}
	else if(IsAbsolutePath(path))
	{
		SaveAbsoluteStream(path,stream);
		return;
	}
	SaveAbsoluteStream(m_workingDictionary+path,stream);
}

//------------------------------------------------------------------------
bool
csArchiveSystem::mount(const csString& url, const csString& archive, csEArchiveType type)
{
	if(m_mountedArchives.end()!=m_mountedArchives.find(url))
	{
		return false;
	}
	csIArchive* arc = csNULL;
	switch(type)
	{
	case csEAT_FILE:	arc = _createFileArchive(archive);	break;
	case csEAT_ZIP:		arc = _createFileArchive(archive);	break;
	default:			return false;
	}
	m_mountedArchives.set(url,arc);
	return true;
}

//------------------------------------------------------------------------
void
csArchiveSystem::unmount(const csString &url)
{
	m_mountedArchives.remove(url);
}

//------------------------------------------------------------------------
csArray<csString>
csArchiveSystem::listMountedArchivesUrl() const
{
	csArray<csString> urls;
	csMountedArchives::const_iterator it = m_mountedArchives.begin();
	while(it)
	{
		urls.push_back(it.key());
	}
	return urls;
}

//------------------------------------------------------------------------
bool
csArchiveSystem::_urlValid(const csString &url) const
{
	if(url.find(csT(':'))
		||url.find(csT('.'))
		||url.find(csT('\\'))
		||url.find(csT('/'))
		||url.length()<2)
	{
		return false;
	}
	return true;
}

//------------------------------------------------------------------------
csIArchive*
csArchiveSystem::_createFileArchive(const csString &path) const
{
	return csNEW csFileArchive(path);
}

//------------------------------------------------------------------------
csIArchive*
csArchiveSystem::_createZipArchive(const csString &path) const
{
	return csNEW csZipArchive(path);
}